#ifndef  _MMU_H
#define  _MMU_H

#include "type.h"
#include "asm/barrier.h"

#define PAGE_SHIFT	 		    12
#define TABLE_SHIFT 			    9
#define SECTION_SHIFT			    (PAGE_SHIFT + TABLE_SHIFT)

#define PAGE_SIZE   			    (1 << PAGE_SHIFT)	
#define SECTION_SIZE			    (1 << SECTION_SHIFT)	


#ifndef __ASSEMBLER__

#define MT_DEVICE_nGnRnE   0
#define MT_DEVICE_nGnRE    1
#define MT_DEVICE_GRE      2
#define MT_NORMAL_NC       3
#define MT_NORMAL          4
#define MT_NORMAL_WT       5

#define PTE_TYPE_PAGE      (UL(3) << 0)
#define PTE_PXN            (BITUL(53))
#define PTE_UXN            (BITUL(54))
#define PTE_AF             (BITUL(10))
#define PTE_RDONLY         (BITUL(7))
#define PTE_SHARED         (UL(3) << 8)
#define PTE_DMB            (BITUL(51))
#define PTE_WRITE          (PTE_DMB)
#define PTE_ATTRINDX(t)    ((t) << 2)
#define PTE_DIRTY          (BITUL(55))

#define PROT_DEFAULT       (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
#define PROT_BASE          (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE)

/* for device memory */
#define PROT_DEVICE_nGnRnE (PROT_BASE | PTE_ATTRINDX(MT_DEVICE_nGnRnE))

/* for normal memory */
#define PROT_NORMAL        (PROT_BASE | PTE_ATTRINDX(MT_NORMAL))
#define PAGE_KERNEL        (PROT_NORMAL)
#define PAGE_KERNEL_RO	   ((PROT_NORMAL & ~PTE_WRITE) | PTE_RDONLY)
#define PAGE_KERNEL_ROX    ((PROT_NORMAL & ~(PTE_WRITE | PTE_PXN)) | PTE_RDONLY)
#define PAGE_KERNEL_EXEC   (PROT_NORMAL & ~PTE_PXN)

#define PGD_IDX(x)    (((BITUL(48)-1) & (u64)(x)) >> 39)
#define PUD_IDX(x)    (((BITUL(39)-1) & (u64)(x)) >> 30)
#define PMD_IDX(x)    (((BITUL(30)-1) & (u64)(x)) >> 21)
#define PTE_IDX(x)    (((BITUL(21)-1) & (u64)(x)) >> 12)

#define PAGE_MASK               (~((UL(1)<<PAGE_SHIFT)-1))
#define PAGE_ALIGN(x)           ((void*)(((u64)(x)) & PAGE_MASK))

typedef  u64   pgdval_t;
typedef  u64   pudval_t;
typedef  u64   pmdval_t;
typedef  u64   pteval_t;

typedef struct {
	pgdval_t pgd;
} pgd_t;
#define pgd_val(x)                        ((x).pgd)
#define pgd_val_addr(x)                   ((pud_t*)PAGE_ALIGN(pgd_val(x)))
#define __pgd(x)                          ((pgd_t){ (x) })
#define pgd_offset_raw(pgdir, virt)       (pgdir + PGD_IDX(virt))
#define PGDIR_SIZE                        (BITUL(39))
#define PGDIR_MASK                        (~(BITUL(39) - 1))
#define pgd_addr_end(addr, end)                                   \
({ unsigned long __boundry = ((addr) + PGDIR_SIZE) & PGDIR_MASK;  \
    (__boundry < end) ? __boundry : end;                          \
})
#define PGD_TYPE_TABLE                     (UL(3) << 0)


typedef struct {
	pudval_t pud;
} pud_t;
#define pud_val(x)                        ((x).pud)
#define pud_val_addr(x)                   ((pmd_t*)PAGE_ALIGN(pud_val(x)))
#define __pud(x)                          ((pud_t){ (x) })
#define pud_offset_phys(pgdp, virt)       (pgd_val_addr(*pgdp) + PUD_IDX(virt))
#define PUDIR_SIZE                        (BITUL(30))
#define PUDIR_MASK                        (~(BITUL(30) - 1))
#define pud_addr_end(addr, end)                                   \
({ unsigned long __boundry = ((addr) + PUDIR_SIZE) & PUDIR_MASK;  \
    (__boundry < end) ? __boundry : end;                          \
})
#define PUD_TYPE_TABLE                     (UL(3) << 0)


typedef struct {
	pmdval_t pmd;
} pmd_t;
#define pmd_val(x)                        ((x).pmd)
#define pmd_val_addr(x)                   ((pte_t*)PAGE_ALIGN(pmd_val(x)))
#define __pmd(x)                          ((pmd_t){ (x) })
#define pmd_offset_phys(pudp, virt)       (pud_val_addr(*pudp) + PMD_IDX(virt))
#define PMDIR_SIZE      (BITUL(21))
#define PMDIR_MASK      (~(BITUL(21) - 1))
#define pmd_addr_end(addr, end)                                   \
({ unsigned long __boundry = ((addr) + PMDIR_SIZE) & PMDIR_MASK;  \
    (__boundry < end) ? __boundry : end;                          \
})
#define PMD_TYPE_TABLE  (UL(3) << 0)


typedef struct {
	pteval_t pte;
} pte_t;
#define pte_val(x) ((x).pte)
#define __pte(x)   ((pte_t){ (x) })
#define pte_offset_phys(pmdp, virt)       (pmd_val_addr(*pmdp) + PTE_IDX(virt))

#define pfn_pte(pfn, prot)  (__pte(((pfn) << PAGE_SHIFT) | (prot)))      
                                                                         
static inline void set_pgd(pgd_t *pgdp, pgd_t pgd)                       
{ 
        *pgdp = pgd;
                                                                         
        dsb(ishst);                                                      
}                                                                        
  
static inline void set_pud(pud_t *pudp, pud_t pud)
{                                    
        *pudp = pud;                 
                                     
        dsb(ishst);                  
}
                                                            
static inline void set_pmd(pmd_t *pmdp, pmd_t pmd)
{                                                           
        *pmdp = pmd;                           

        dsb(ishst);                                  
}
                                            
static inline void set_pte(pte_t *ptep, pte_t pte)
{
        *ptep = pte;                                              
        dsb(ishst);                                                                                       
}
  
#define pgd_none(pgd) (!pgd_val(pgd))
#define pud_none(pud) (!pud_val(pud))
#define pmd_none(pmd) (!pmd_val(pmd))
#define ptd_none(ptd) (!ptd_val(ptd))

void __create_pgd_mapping(pgd_t* pgdir, u64 paddr, u64 vaddr, u64 size, u64 prot);

#endif /* __ASSENBLER__ */


#endif /* _MMU_H */

